#if 0 // 备份
/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include <string.h>
//#include <inttypes.h>
#include "sdkconfig.h" //sdk配置
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"           //任务
#include "freertos/message_buffer.h" //使用消息缓冲区必须要有这个头文件
#include "esp_chip_info.h"           //芯片_信息
#include "esp_flash.h"               //内存
#include "esp_system.h"              //系统
#include "esp_spi_flash.h"
#include "driver/uart.h" //串口
#include "esp_log.h"     //日志

#include "gatts_table_creat_demo.h" //ble蓝牙

#include "driver/gpio.h"  //GPIO
#include "driver/ledc.h"  //LEDPWM
#include "driver/mcpwm.h" //电机LEDPWM


static const char *TAG = "example1";
static ledc_timer_config_t dingShiQi; // 定时器

#define led_pin GPIO_NUM_2
#define led_deng_dai GPIO_NUM_12
#define led_deng_dai_gao GPIO_NUM_13

/*串口*/
#define UART1_TX_BUF_SIZE 256 /* 256个字节 */
#define UART1_RX_BUF_SIZE 132 /* 256个字节 */
#define UART1_RX GPIO_NUM_16  /* 256个字节 */
#define UART1_TX GPIO_NUM_17  /* 256个字节 */
static char msg_test[] = "hello,world"; /* 测试使用的字符串 */
static char buffer[UART1_RX_BUF_SIZE];  /* 暂时存储从串口接收到的字符串 */

/*串口初始化*/
void uartInit()
{
    /* 串口1的配置 */
    const uart_config_t uart1_config =
        {
            .baud_rate = 115200,                   /* 通讯波特率 */
            .data_bits = UART_DATA_8_BITS,         /* 每一个数据是8位 */
            .parity = UART_PARITY_DISABLE,         /* 关闭奇偶校验 */
            .stop_bits = UART_STOP_BITS_1,         /* 停止位是1位 */
            .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, /* 软件控流 */
            .source_clk = UART_SCLK_APB,           /* APB时钟 */
        };
    /* 设置串口1的参数 */
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart1_config));

    /* 设置串口的gpio口，esp32支持gpio口动态设置，这一次先使用默认的串口gpio */
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, UART1_TX, UART1_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

    /* 启动串口1 */
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1,        /* 串口1 */
                                        UART1_TX_BUF_SIZE, /* 发送FIFO的大小 */
                                        UART1_RX_BUF_SIZE, /* 接受FIFO的大小 */
                                        0,                 /* 不使用queue */
                                        NULL,              /* 因为不使用queue，所以NULL */
                                        0)                 /* 不分配中断标志 */
    );
}

/*LEDPWM初始化*/
void led_pwm_Config()
{
    ledc_timer_config_t ledc_timer_Structure;
    ledc_timer_Structure.speed_mode = LEDC_HIGH_SPEED_MODE;
    ledc_timer_Structure.timer_num = LEDC_TIMER_0;
    ledc_timer_Structure.duty_resolution = 13; // 分辨率
    ledc_timer_Structure.freq_hz = 5000;
    ledc_timer_Structure.clk_cfg = LEDC_APB_CLK;
    ledc_timer_config(&ledc_timer_Structure);
    ledc_channel_config_t ledc_channel_Structure;
    ledc_channel_Structure.gpio_num = led_deng_dai_gao; // 16
    ledc_channel_Structure.speed_mode = LEDC_HIGH_SPEED_MODE;
    ledc_channel_Structure.channel = LEDC_CHANNEL_0;
    ledc_channel_Structure.intr_type = LEDC_INTR_DISABLE;
    ledc_channel_Structure.timer_sel = 0;
    ledc_channel_Structure.duty = 0;
    ledc_channel_Structure.hpoint = 0;
    ledc_channel_config(&ledc_channel_Structure);
    // 若想改变占空比，用这个。
    // ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 8190 * 0.4); // 负占空比，负值为50/256=19.5%; 0-8190
    // ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
}
/*发送LEDPWM方波*/
void ledPwmTianYao(float zhanKongBi)
{
    ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 8190 * zhanKongBi); // 负占空比，负值为50/256=19.5%; 0-8190
    ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
}

/*MCPWM方波*/
void tianYao(float zhanKongBi)
{
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, led_deng_dai_gao); // 一台 MCPWM 发生器就足够了
    mcpwm_config_t pwm_config = {
        .frequency = 2000, // 频率=2kHz
        .cmpr_a = 0,       // PWMxA 的占空比 = 0
        .counter_mode = MCPWM_UP_COUNTER,
        .duty_mode = MCPWM_DUTY_MODE_0,
    };
    mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);
    float aaa = 0;
    bool ac = true;
    while (ac)
    {
        ESP_LOGI(TAG, "现在的占空比为: %f", aaa);
        ESP_ERROR_CHECK(mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_GEN_A, aaa));
        if (aaa > 98)
        {
            ac = false;
        }
        aaa = aaa + 10;
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 多线程,定义消息缓冲区句柄
// MessageBufferHandle_t xMessageBuffer;
//const size_t xMessageBufferSizeBytes = 100;
MessageBufferHandle_t xMessageBuffer;
static const size_t xMessageBufferSizeBytes = 100;

// 多线程,创建队列并发布值的任务
void vATask(void *pvParameters)
{
    const uart_port_t uart_num = UART_NUM_1;
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
        .rx_flow_ctrl_thresh = 122,
    };
    // Configure UART parameters
    ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
    /*********************/
    size_t xBytesSent;
    uint8_t ucArrayToSend[] = {'0', '1', '2', '3', 0};
    // unsigned char
    char *pcStringToSend = "String to send";
    const TickType_t x100ms = pdMS_TO_TICKS(100); // 延时100ms

    int len = 0;

    while (1)
    { // 消息缓存区发送的第一种方式
        
        len = uart_read_bytes(UART_NUM_1, buffer, (UART1_RX_BUF_SIZE - 1), 20 / portTICK_PERIOD_MS); // 从串口1的RX_FIFO获取字符串
        // 如果读到包的话
        if (len)
        {
            printf("len  =======   %d\n", len);
            buffer[len] = '\0';                                                             // 在结尾加入字符'\0'，
            ESP_LOGI(TAG, "Recv str -> %s , and the length is:%d", buffer, strlen(buffer)); // 打印logo
            // uart_write_bytes(UART_NUM_1, buffer, strlen(buffer));//发送数据到串口1
            //  将数组发送到消息缓冲区，最多阻塞 100 毫秒，以等待消息缓冲区中有足够的可用空间。
            // xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)buffer, sizeof(buffer), x100ms);
            xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)buffer, len + 1, x100ms);
            // printf("buffer  =======   %d\n",strlen(buffer)+1);

            if (xBytesSent != sizeof(buffer))
            {
                // 在缓冲区中有足够的空间用于写入数据之前，对 xMessageBufferSend() 的调用超时。
            }
            // uart_write_bytes(UART_NUM_1, buffer, strlen(buffer));//发送数据到串口1
            memset(buffer, 0, sizeof(buffer)); // 清空内存，等待下一次的串口报文。
        }

        /*
         // 将数组发送到消息缓冲区，最多阻塞 100 毫秒，以等待消息缓冲区中有足够的可用空间。
         xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)ucArrayToSend, sizeof(ucArrayToSend), x100ms);

         if (xBytesSent != sizeof(ucArrayToSend))
         {
             // 在缓冲区中有足够的空间用于写入数据之前，对 xMessageBufferSend() 的调用超时。
         }

         // 消息缓存区发送的第二种方式
         //  将字符串发送到消息缓冲区。如果缓冲区空间不足，则立即返回。strlen 计算字符串长度不含结尾"\0"
         xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)pcStringToSend, strlen(pcStringToSend), 0);
         if (xBytesSent != strlen(pcStringToSend))
         {
         }
         */

        // vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

// 多线程,从队列接收的任务。
void vADifferentTask(void *pvParameters)
{//vTaskDelete(NULL)可以用于删除任务自身。
    uint8_t ucRxData[20];
    size_t xReceivedBytes;
    const TickType_t xBlockTime = pdMS_TO_TICKS(20);

    char *pcStringToSend = "String to send";
    float zkbGaiBian = 0;
    while (1)
    {
        // 接收消息
        // 从消息缓冲区接收下一条消息。在阻塞状态下等待（因此不使用任何 CPU 处理时间）最多 100 毫秒，消息才可用。
        xReceivedBytes = xMessageBufferReceive(xMessageBuffer,
                                               (void *)ucRxData,
                                               sizeof(ucRxData),
                                               xBlockTime);

        if (xReceivedBytes > 0)
        {
            printf(" jie-----shou: ucRxData is %d long. ucRxData : %s \n", xReceivedBytes, ucRxData);
            char zkbJieShou = ucRxData[0];
            switch (zkbJieShou)
            {
            case 'a':
                zkbGaiBian = 20;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            case 'b':
                zkbGaiBian = 40;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            case 'c':
                zkbGaiBian = 60;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            case 'd':
                zkbGaiBian = 80;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            case 'e':
                zkbGaiBian = 98;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            case 'f':
                zkbGaiBian = 0;
                ESP_LOGI(TAG, "现在的占空比为: %f", zkbGaiBian);
                break;
            default:
                break;
            }
            // 这里的消息......
            // uart_write_bytes(UART_NUM_1, pcStringToSend, strlen(pcStringToSend));//发送数据到串口1
            // ESP_LOGI(TAG, "Recv shi li  -> %s ,  555555 and the length is:%d", msg_test, strlen(msg_test));
        }
        ledPwmTianYao(zkbGaiBian);
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

// GPIO初始化
void gpioInit(void)
{
    gpio_reset_pin(led_pin); // 清除 GPIO 默认的状态
    gpio_reset_pin(led_deng_dai_gao);
    gpio_reset_pin(led_deng_dai);

    // gpio_set_direction(led_deng_dai_gao, GPIO_MODE_OUTPUT); // GPIO 输出模式
    gpio_set_direction(led_pin, GPIO_MODE_OUTPUT);            // GPIO 输出模式
    gpio_set_direction(led_deng_dai, GPIO_MODE_INPUT_OUTPUT); // GPIO 输出模式
}

void app_main(void)
{
    printf("Hello world!\n");

    /****************************************/

    /* *******************Print chip information */
    esp_chip_info_t chip_info;
    uint32_t flash_size;
    esp_chip_info(&chip_info);
    printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
           CONFIG_IDF_TARGET,
           chip_info.cores,
           (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
           (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
           (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
           (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");

    unsigned major_rev = chip_info.revision / 100;
    unsigned minor_rev = chip_info.revision % 100;
    printf("silicon revision v%d.%d, ", major_rev, minor_rev);
    if (esp_flash_get_size(NULL, &flash_size) != ESP_OK)
    {
        printf("Get flash size failed");
        return;
    }

    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
           (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
    /***************** Print chip information */
    gpioInit();
    led_pwm_Config();
    
    uartInit();


    // 启动线程
    xMessageBuffer = xMessageBufferCreate(xMessageBufferSizeBytes);
    if (xMessageBuffer == NULL)
    {
        printf("message buffer Create fail!\n");
    }
    else
    {
        printf("message buffer Create successed!\n");
    }
    xTaskCreate(vATask, "vATask", 1024 * 2, NULL, 12, NULL);
    xTaskCreate(vADifferentTask, //指向任务入口函数的指针。任务必须实现为永不返回（即连续循环），或者应该使用 vTaskDelete 函数终止
                "vADifferentTask",//任务的描述性名称。这主要是为了*方便调试。configMAX_TASK_NAME_LEN 定义的最大长度 - 默认 * 为 16。
                1024 * 2, //任务堆栈的大小指定为 * 字节数。请注意，这与普通 FreeRTOS 不同。
                NULL, //将用作正在创建的任务的参数的指针。
                11, //任务应运行的优先级。
                NULL);//用于传回一个句柄，通过该句柄可以引用创建的任务

    app_bt_aa();
   // uart_write_bytes(UART_NUM_1, msg_test, strlen(msg_test)); // 发送数据到串口1

    for (;;)
    {
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
    // int aa = jiafa2(6, 8);
    float zkb = 0;
    // ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 8190 * 0.4); // 负占空比，负值为50/256=19.5%; 0-8190
    // ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
    for (int i = 10; i >= 0; i--)
    {
        printf("Restarting in %d seconds...\n", i);
        // printf("jieguo= %d\n", aa);
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 定时器延时
        gpio_set_level(led_pin, 0);            // 输出低电平
        zkb = zkb + 0.1;

        if (i < 2)
        {
            i = 20;
        }
        if (zkb > 0.9)
        {
            zkb = 0.1;
        }

        vTaskDelay(1000 / portTICK_PERIOD_MS);
        gpio_set_level(led_pin, 1); // 输出高电平
        ESP_LOGI(TAG, "现在的占空比为: %f", zkb);
        ledPwmTianYao(zkb);
        // ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 8190 * 0.4); // 负占空比，负值为50/256=19.5%; 0-8190
        // ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
    }

    printf("Restarting now.\n");
    fflush(stdout);
    esp_restart();
}

#elif 0 // 备份BTgatts_table_creat_demo.c
/*
 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

/****************************************************************************
 *
 * This demo showcases creating a GATT database using a predefined attribute table.
 * It acts as a GATT server and can send adv data, be connected by client.
 * Run the gatt_client demo, the client demo will automatically connect to the gatt_server_service_table demo.
 * Client demo will enable GATT server's notify after connection. The two devices will then exchange
 * data.
 *
 ****************************************************************************/

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/message_buffer.h" //使用消息缓冲区必须要有这个头文件
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"
#include "gatts_table_creat_demo.h"
#include "esp_gatt_common_api.h"

// #include "driver/uart.h"             //串口

#define GATTS_TABLE_TAG "GATTS_TABLE_DEMO"
#define GATTS_TABLE_JS "jie-shou: "
#define GATTS_TABLE_JS2 "jie-shou2: "
#define GATTS_TABLE_JS3 "jie-shou3: "

#define PROFILE_NUM 1
#define PROFILE_APP_IDX 0
#define ESP_APP_ID 0x55
#define SAMPLE_DEVICE_NAME "ESP_GATTS_DEMO" // BT被别的设备发现显示的名字
#define SVC_INST_ID 0

/* 特征值的最大长度。当GATT客户端执行写入或准备写入操作时，数据长度必须小于GATTS_DEMO_CHAR_VAL_LEN_MAX
 */
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 500
#define PREPARE_BUF_MAX_SIZE 1024
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))

#define ADV_CONFIG_FLAG (1 << 0)
#define SCAN_RSP_CONFIG_FLAG (1 << 1)

static uint8_t adv_config_done = 0;

uint16_t heart_rate_handle_table[HRS_IDX_NB];

typedef struct
{
    uint8_t *prepare_buf;
    int prepare_len;
} prepare_type_env_t;

static prepare_type_env_t prepare_write_env;

#define CONFIG_SET_RAW_ADV_DATA
#ifdef CONFIG_SET_RAW_ADV_DATA
static uint8_t raw_adv_data[] = {
    /* flags */ /* 0x02数据长度, BT模式- 0x01 模式,0x06数据*/
    0x02, 0x01, 0x06,
    /* tx power*/ /*发射功率: 0x02数据长度, 0a发射功率等级, */
    0x02, 0x0a, 0xeb,
    /* service uuid */ /*uuid 0x03表示后面有3个数据,后一个0x03表示是1个16比特的UUID*/
    0x03, 0x03, 0xFF, 0x00,
    /* device name */ /*BT广播数据里的名字,0x0f 后面的数据长度,0x09BT本地设备名字*/
    0x0f, 0x09, 'E', 'S', 'P', '_', 'G', 'A', 'T', 'T', 'S', '_', 'D', 'E', 'M', 'O'};
static uint8_t raw_scan_rsp_data[] = {
    /* flags */
    0x02, 0x01, 0x06,
    /* tx power */
    0x02, 0x0a, 0xeb,
    /* service uuid */
    0x03, 0x03, 0xFF, 0x00};

#else
static uint8_t service_uuid[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    // first uuid, 16bit, [12],[13] is the value
    0xfb,
    0x34,
    0x9b,
    0x5f,
    0x80,
    0x00,
    0x00,
    0x80,
    0x00,
    0x10,
    0x00,
    0x00,
    0xFF,
    0x00,
    0x00,
    0x00,
};

/* The length of adv data must be less than 31 bytes */
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x0006, // slave connection min interval, Time = min_interval * 1.25 msec
    .max_interval = 0x0010, // slave connection max interval, Time = max_interval * 1.25 msec
    .appearance = 0x00,
    .manufacturer_len = 0,       // TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, // test_manufacturer,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(service_uuid),
    .p_service_uuid = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp = true,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x0006,
    .max_interval = 0x0010,
    .appearance = 0x00,
    .manufacturer_len = 0,       // TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, //&test_manufacturer[0],
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(service_uuid),
    .p_service_uuid = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
#endif /* CONFIG_SET_RAW_ADV_DATA */

static esp_ble_adv_params_t adv_params = {
    .adv_int_min = 0x20,
    .adv_int_max = 0x40,
    .adv_type = ADV_TYPE_IND,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .channel_map = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

struct gatts_profile_inst
{
    esp_gatts_cb_t gatts_cb; // GATT 服务器回调函数类型,函数指针
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

/*此函数为函数指针,事件发生时的回调*/
static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
                                        esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/*1个基于gatt的配置文件1个app_id和1个gatts_if，该数组将存储ESP_GATTS_REG_EVT返回的gatts_if */
static struct gatts_profile_inst heart_rate_profile_tab[PROFILE_NUM] = {
    [PROFILE_APP_IDX] = {
        .gatts_cb = gatts_profile_event_handler,
        .gatts_if = ESP_GATT_IF_NONE, /* 没有得到gatt_if，所以初始是ESP_GATT_IF_NONE */
    },
};

/* Service */
static const uint16_t GATTS_SERVICE_UUID_TEST = 0x00FF;
static const uint16_t GATTS_CHAR_UUID_TEST_A = 0xFF01;
static const uint16_t GATTS_CHAR_UUID_TEST_B = 0xFF02;
static const uint16_t GATTS_CHAR_UUID_TEST_C = 0xFF03;

static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ;
static const uint8_t char_prop_write = ESP_GATT_CHAR_PROP_BIT_WRITE;
static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t heart_measurement_ccc[2] = {0x00, 0x00};
static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};

/* Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
    {
        // Service Declaration
        [IDX_SVC] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},

        /* Characteristic Declaration */
        [IDX_CHAR_A] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},

        /* Characteristic Value */
        [IDX_CHAR_VAL_A] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},

        /* Client Characteristic Configuration Descriptor */
        [IDX_CHAR_CFG_A] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},

        /* Characteristic Declaration */
        [IDX_CHAR_B] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}},

        /* Characteristic Value */
        [IDX_CHAR_VAL_B] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},

        /* Characteristic Declaration */
        [IDX_CHAR_C] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_write}},

        /* Characteristic Value */
        [IDX_CHAR_VAL_C] =
            {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_C, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},

};

/*回调函数*/
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    switch (event)
    {
#ifdef CONFIG_SET_RAW_ADV_DATA
    case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
        adv_config_done &= (~ADV_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&adv_params);
        }
        break;
    case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
        adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&adv_params);
        }
        break;
#else
    case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
        adv_config_done &= (~ADV_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&adv_params);
        }
        break;
    case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
        adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&adv_params);
        }
        break;
#endif
    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        /* advertising start complete event to indicate advertising start successfully or failed */
        if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "advertising start failed");
        }
        else
        {
            ESP_LOGI(GATTS_TABLE_TAG, "advertising start successfully");
        }
        break;
    case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
        if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "Advertising stop failed");
        }
        else
        {
            ESP_LOGI(GATTS_TABLE_TAG, "Stop adv successfully\n");
        }
        break;
    case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
                 param->update_conn_params.status,
                 param->update_conn_params.min_int,
                 param->update_conn_params.max_int,
                 param->update_conn_params.conn_int,
                 param->update_conn_params.latency,
                 param->update_conn_params.timeout);
        break;
    default:
        break;
    }
}

void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
{
    ESP_LOGI(GATTS_TABLE_TAG, "prepare write, handle = %d, value len = %d", param->write.handle, param->write.len);
    esp_gatt_status_t status = ESP_GATT_OK;
    if (prepare_write_env->prepare_buf == NULL)
    {
        prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
        prepare_write_env->prepare_len = 0;
        if (prepare_write_env->prepare_buf == NULL)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "%s, Gatt_server prep no mem", __func__);
            status = ESP_GATT_NO_RESOURCES;
        }
    }
    else
    {
        if (param->write.offset > PREPARE_BUF_MAX_SIZE)
        {
            status = ESP_GATT_INVALID_OFFSET;
        }
        else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE)
        {
            status = ESP_GATT_INVALID_ATTR_LEN;
        }
    }
    /*send response when param->write.need_rsp is true */
    if (param->write.need_rsp)
    {
        esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
        if (gatt_rsp != NULL)
        {
            gatt_rsp->attr_value.len = param->write.len;
            gatt_rsp->attr_value.handle = param->write.handle;
            gatt_rsp->attr_value.offset = param->write.offset;
            gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
            memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
            esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
            if (response_err != ESP_OK)
            {
                ESP_LOGE(GATTS_TABLE_TAG, "Send response error");
            }
            free(gatt_rsp);
        }
        else
        {
            ESP_LOGE(GATTS_TABLE_TAG, "%s, malloc failed", __func__);
        }
    }
    if (status != ESP_GATT_OK)
    {
        return;
    }
    memcpy(prepare_write_env->prepare_buf + param->write.offset,
           param->write.value,
           param->write.len);
    prepare_write_env->prepare_len += param->write.len;
}

void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
{
    if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf)
    {
        // ESP_LOG_BUFFER_CHAR
        esp_log_buffer_hex(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len);
    }
    else
    {
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATT_PREP_WRITE_CANCEL");
    }
    if (prepare_write_env->prepare_buf)
    {
        free(prepare_write_env->prepare_buf);
        prepare_write_env->prepare_buf = NULL;
    }
    prepare_write_env->prepare_len = 0;
}

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    size_t xMessagSizeBytes = 100;
    switch (event)
    {
    case ESP_GATTS_REG_EVT:
    {
        esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
        if (set_dev_name_ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "set device name failed, error code = %x", set_dev_name_ret);
        }
#ifdef CONFIG_SET_RAW_ADV_DATA
        esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
        if (raw_adv_ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "config raw adv data failed, error code = %x ", raw_adv_ret);
        }
        adv_config_done |= ADV_CONFIG_FLAG;
        esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data));
        if (raw_scan_ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "config raw scan rsp data failed, error code = %x", raw_scan_ret);
        }
        adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#else
        // config adv data
        esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
        if (ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
        }
        adv_config_done |= ADV_CONFIG_FLAG;
        // config scan response data
        ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
        if (ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "config scan response data failed, error code = %x", ret);
        }
        adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#endif
        esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);
        if (create_attr_ret)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "create attr table failed, error code = %x", create_attr_ret);
        }
    }
    break;
    case ESP_GATTS_READ_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT");
        break;
    case ESP_GATTS_WRITE_EVT:
        if (!param->write.is_prep)
        {
            /********************************************************************************/

            size_t xBytesSent;
            unsigned char *pcString = param->write.value;
            void *zhuanHuan = (void *)pcString;
            char *pcStringToSend = (char *)zhuanHuan;
            const TickType_t x100ms = pdMS_TO_TICKS(100);
            uint8_t ucArrayToSend[] = {'0', '1', '2', '3', 0};
            // 创建消息缓冲区

            /*xMessageBuffer = xMessageBufferCreate(xMessagSizeBytes);
            if (xMessageBuffer == NULL)
            {
                printf("message buffer Create fail!\n");
            }
            else
            {
                printf("message buffer Create successed!\n");
            }*/
            // 将数组发送到消息缓冲区，最多阻塞 100 毫秒，以等待消息缓冲区中有足够的可用空间。
            xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)pcStringToSend, strlen(pcStringToSend), x100ms);

            /*****************/
            // gattc写入的数据长度必须小于GATTS_DEMO_CHAR_VAL_LEN_MAX.
            ESP_LOGI(GATTS_TABLE_JS, "GATT_WRITE_EVT, handle = %d, value len = %d, value :", param->write.handle, param->write.len);
            ESP_LOGI(GATTS_TABLE_JS, " = %s, \n", param->write.value);
            esp_log_buffer_hex(GATTS_TABLE_JS,     // 描述标签
                               param->write.value, // 指向缓冲区数组的指针
                               param->write.len);  // 缓冲区的长度（以字节为单位）
            if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->write.handle && param->write.len == 2)
            {
                uint16_t descr_value = param->write.value[1] << 8 | param->write.value[0];
                if (descr_value == 0x0001)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
                    uint8_t notify_data[15];
                    for (int i = 0; i < sizeof(notify_data); ++i)
                    {
                        notify_data[i] = i % 0xff;
                    }
                    // notification_data[] 的大小需要小于 MTU 大小
                    esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
                                                sizeof(notify_data), notify_data, false);
                }
                else if (descr_value == 0x0002)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
                    uint8_t indicate_data[15];
                    for (int i = 0; i < sizeof(indicate_data); ++i)
                    {
                        indicate_data[i] = i % 0xff;
                    }
                    // the size of indicate_data[] need less than MTU size
                    esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
                                                sizeof(indicate_data), indicate_data, true);
                }
                else if (descr_value == 0x0000)
                {
                    ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable ");
                }
                else
                {
                    ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
                    esp_log_buffer_hex(GATTS_TABLE_JS2, param->write.value, param->write.len);
                }
            }
            /* send response when param->write.need_rsp is true*/
            if (param->write.need_rsp)
            {
                esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
            }
        }
        else
        {
            /* handle prepare write */
            example_prepare_write_event_env(gatts_if, &prepare_write_env, param);
        }
        break;
    case ESP_GATTS_EXEC_WRITE_EVT:
        // the length of gattc prepare write data must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT");
        example_exec_write_event_env(&prepare_write_env, param);
        break;
    case ESP_GATTS_MTU_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
        break;
    case ESP_GATTS_CONF_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONF_EVT, status = %d, attr_handle %d", param->conf.status, param->conf.handle);
        break;
    case ESP_GATTS_START_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
        break;
    case ESP_GATTS_CONNECT_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);
        esp_log_buffer_hex(GATTS_TABLE_JS3, param->connect.remote_bda, 6);
        esp_ble_conn_update_params_t conn_params = {0};
        memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
        /* For the iOS system, please refer to Apple official documents about the BLE connection parameters restrictions. */
        conn_params.latency = 0;
        conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
        conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
        conn_params.timeout = 400;  // timeout = 400*10ms = 4000ms
        // start sent the update connection parameters to the peer device.
        esp_ble_gap_update_conn_params(&conn_params);
        break;
    case ESP_GATTS_DISCONNECT_EVT:
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason);
        esp_ble_gap_start_advertising(&adv_params);
        break;
    case ESP_GATTS_CREAT_ATTR_TAB_EVT:
    {
        if (param->add_attr_tab.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
        }
        else if (param->add_attr_tab.num_handle != HRS_IDX_NB)
        {
            ESP_LOGE(GATTS_TABLE_TAG, "create attribute table abnormally, num_handle (%d) \
                        doesn't equal to HRS_IDX_NB(%d)",
                     param->add_attr_tab.num_handle, HRS_IDX_NB);
        }
        else
        {
            ESP_LOGI(GATTS_TABLE_TAG, "create attribute table successfully, the number handle = %d\n", param->add_attr_tab.num_handle);
            memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table));
            esp_ble_gatts_start_service(heart_rate_handle_table[IDX_SVC]);
        }
        break;
    }
    case ESP_GATTS_STOP_EVT:
    case ESP_GATTS_OPEN_EVT:
    case ESP_GATTS_CANCEL_OPEN_EVT:
    case ESP_GATTS_CLOSE_EVT:
    case ESP_GATTS_LISTEN_EVT:
    case ESP_GATTS_CONGEST_EVT:
    case ESP_GATTS_UNREG_EVT:
    case ESP_GATTS_DELETE_EVT:
    default:
        break;
    }
}

/*事件回调函数*/
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{

    /* 如果事件是注册事件，则存储每个配置文件的 gatts_if */
    if (event == ESP_GATTS_REG_EVT)
    {
        if (param->reg.status == ESP_GATT_OK)
        {
            heart_rate_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if;
        }
        else
        {
            ESP_LOGE(GATTS_TABLE_TAG, "reg app failed, app_id %04x, status %d",
                     param->reg.app_id,
                     param->reg.status);
            return;
        }
    }
    do
    {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++)
        {
            /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
            if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile_tab[idx].gatts_if)
            {
                if (heart_rate_profile_tab[idx].gatts_cb)
                {
                    heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
                }
            }
        }
    } while (0);
}

void app_bt_aa(void)
{
    esp_err_t ret;

    /* 初始化NVS. */
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "%s chu shi hua kong zhi qi: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "%s qi yong kong zhi qi: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_init();
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "%s chu shi hua la ya zi yuan: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_enable();
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "%s qi yong lan ya: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "zhu ce hui diao = %x", ret);
        return;
    }

    ret = esp_ble_gap_register_callback(gap_event_handler);
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "sao miao hui diao = %x", ret);
        return;
    }

    ret = esp_ble_gatts_app_register(ESP_APP_ID);
    if (ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "zu ce ying yong biao shi = %x", ret);
        return;
    }

    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
    if (local_mtu_ret)
    {
        ESP_LOGE(GATTS_TABLE_TAG, "she zhi ben di MTU = %x", local_mtu_ret);
    }
}

#elif 0

#include <stdio.h>
#define SEEK_SET 0 /* set file offset to offset */

#include <stdio.h>

#include <stdio.h>
// #include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_log.h"
#include "freertos/event_groups.h"

#include "freertos/semphr.h"
// 线程池池部结构体
#define THREAD_NUMBER_MAX 16
typedef struct ST_THREAD_POOL_
{
    int threadNumber;                                  // 当前线程个数
    void *threadReadLock;                              // 当前各个线程读锁
    void *threadWriteLock;                             // 当前操作线程写锁
    void *threadPollQueue;                             // 线程处理队列
    TaskHandle_t threadHandleArray[THREAD_NUMBER_MAX]; // 不用静态数组、可以使用链表

} st_threadPool, *st_threadPool_t;

// 线程池队列式处理
typedef struct ST_THREAD_QUEUE__
{
    int (*function)(void *);
    void *funcArgv;
} st_threadQueum, *st_threadQueum_t;

// 线程池中主处理
static void T_threadPoolFunc(st_threadPool_t tp)
{
    int ret = 0;
    int (*fun)(void *) = NULL;
    st_threadQueum queue = {0};
    while (1)
    {
        xSemaphoreTake(tp->threadReadLock, portMAX_DELAY); // 上锁
        do
        {
            memset(&queue, 0, sizeof(st_threadQueum));
            ret = xQueueReceive(tp->threadPollQueue, &queue, portMAX_DELAY); // 进行队列等待，读取队列数据
        } while (ret == pdFALSE);
        xSemaphoreGive(tp->threadReadLock); // 解锁
        fun = queue.function;
        if (fun)
        {
            ret = (*fun)(queue.funcArgv); // 函数指针回调
            if (ret == -1)
            {
                break;
            } // 返回-1 就会将当前线程释放
        }
    }
    vTaskDelete(NULL);
    return;
}

// 创建线程池任务

void *T_threadPoolCreate(int threadNum, char *name, int stackSize, int priority)
{
    int i = 0;
    int ret = 0;
    char threadName[36] = {0};
    st_threadPool_t tp = (st_threadPool_t)malloc(sizeof(st_threadPool)); // malloc 动态内存分配
    if (!tp)
    {
        goto Err_malloc;
    }

    memset(tp, 0, sizeof(st_threadPool));
    /*
    由于FreeRTOS的新旧版本的API不同，导致现象不同于预期，问题就在xSemaphoreCreateBinary与vSemaphoreCreateBinary的区别
    用vSemaphoreCreateBinary创建的二元信号量，初始值为“满”，因为创建的同时释放了信号量
    需要调用xSemaphoreGive初始化当前信号量
    */
    tp->threadReadLock = xSemaphoreCreateBinary();
    if (!tp->threadReadLock)
    {
        goto Err_rMutex;
    }
    xSemaphoreGive(tp->threadReadLock);

    tp->threadWriteLock = xSemaphoreCreateBinary(); // 创建一个新的二进制信号量实例，并返回一个可以引用新信号量的句柄
    if (!tp->threadWriteLock)
    {
        goto Err_wMutex;
    }
    xSemaphoreGive(tp->threadWriteLock); // 释放信号量

    tp->threadPollQueue = xQueueCreate(threadNum + 1, sizeof(st_threadQueum)); // 创建一个新的队列实例，并返回一个可以引用新队列的句柄
    if (!tp->threadPollQueue)
    {
        goto Err_queue;
    }

    for (i = 0; i < threadNum; i++)
    {
        /*snprintf 是一个C语言中用于格式化输出字符串的函数，它类似于 printf，但是可以将格式化后的字符串输出到一个指定大小的字符数组中，
        从而避免缓冲区溢出的问题。*/
        snprintf(threadName, 36, "tp%s_%d", name, i);
        ret = xTaskCreate(T_threadPoolFunc, threadName, stackSize, tp, priority, &tp->threadHandleArray[i]);
        if (ret != pdPASS)
        {
            goto Err_thFork;
        }
    }
    tp->threadNumber = i;
    return tp;

Err_thFork:
    for (int j = 0; j < i; j++)
    {
        vTaskDelete(&tp->threadHandleArray[i]);
    }
    vQueueDelete(tp->threadPollQueue);
Err_queue:
    vSemaphoreDelete(tp->threadWriteLock);
Err_wMutex:
    vSemaphoreDelete(tp->threadReadLock);
Err_rMutex:
    free(tp);
Err_malloc:
    return NULL;
}

// 等待空闲线程进行任务线程回收
void T_threadPoolDelete(st_threadPool_t tp)
{
    if (!tp)
    {
        return;
    }
    xSemaphoreTake(tp->threadWriteLock, portMAX_DELAY); // 上锁
    tp->threadPollQueue = NULL;
    for (int i = 0; i < tp->threadNumber; i++)
    {
        vTaskDelete(tp->threadHandleArray[i]);
        tp->threadHandleArray[i] = NULL;
    }
    vQueueDelete(tp->threadPollQueue);
    xSemaphoreGive(tp->threadWriteLock);

    vSemaphoreDelete(tp->threadWriteLock);
    tp->threadWriteLock = NULL;
    vSemaphoreDelete(tp->threadReadLock);
    tp->threadReadLock = NULL;
    free(tp);
    tp = NULL;
}

// 向线程池中，发送任务。
int T_threadPoolAddTask(st_threadPool_t tp, int (*func)(void *argv), void *argv, int timeOut_ticks)
{
    int temp = 0;
    st_threadQueum queueObj = {0};
    if (!tp || !tp->threadWriteLock || !tp->threadPollQueue)
    {
        return -1;
    }
    xSemaphoreTake(tp->threadWriteLock, portMAX_DELAY); // 上锁
    queueObj.function = func;
    queueObj.funcArgv = argv;
    temp = xQueueSend(tp->threadPollQueue, &queueObj, timeOut_ticks);
    xSemaphoreGive(tp->threadWriteLock); // 解锁
    return temp;
}

// 测试函数
int test_1(void *argc)
{
    for (int i = 0; i < 100; i++)
    {
        printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__);
        // vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    return 0;
}
int test_2(void *argc)
{
    for (int i = 0; i < 100; i++)
    {
        printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__);
        // vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    return 0;
}
int test_0(void *argc)
{
    for (int i = 0; i < 100; i++)
    {
        printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__);
        // vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    return 0;
}
/*
int app_main( void * argc )
{
    printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
    //os_time_dly( 100 * 5 );
    vTaskDelay(pdMS_TO_TICKS(1000));
    printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
    void* tp_fd = T_threadPoolCreate( 4 , "thTest" , 2 * 1024 , 10 );
    if( tp_fd != NULL )
    {
        T_threadPoolAddTask( tp_fd , test_0 , NULL , 10 );
        T_threadPoolAddTask( tp_fd , test_1 , NULL , 10 );
        T_threadPoolAddTask( tp_fd , test_2 , NULL , 10 );
    }
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    T_threadPoolDelete( tp_fd );
    printf("结束\r\n");
    return 0;
}
*/

#elif 0

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h" //使用消息缓冲区必须要有这个头文件
typedef struct
{
    char *name;
    int age;
} info_t;

//===============================Task实现函数，不同四种参数的传入============================
void My_task1(void *param)
{
    int *data;
    data = (int *)param;
    while (1) // 死循环
    {
        printf("Running Task1! param = %d\n", *data);
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时一段时间
    }
}
void My_task2(void *param)
{
    int *data;
    data = (int *)param;
    printf("Running Task2!param = {%d, %d, %d}\n", *data, *(data + 1), *(data + 2));
    vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时一段时间
    vTaskDelete(NULL);
}
void My_task3(void *param)
{
    char *data;
    data = (char *)param;
    while (1) // 死循环
    {
        printf("Running Task3!param = %s\n", data);
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时一段时间
    }
}

void My_task4(void *param)
{
    info_t *data;
    data = (info_t *)param;
    while (1) // 死循环
    {
        printf("Running Task4!param ={name:%s, age:%d}\n", data->name, data->age);
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时一段时间
    }
}
/***************静态分配自旋锁并使用 portMUX_INITIALIZER_UNLOCKED 初始化：******************/
// 静态分配并初始化自旋锁
static portMUX_TYPE my_spinlock = portMUX_INITIALIZER_UNLOCKED;

void some_function(void)
{
    taskENTER_CRITICAL(&my_spinlock);
    // 此时已处于临界区
    taskEXIT_CRITICAL(&my_spinlock);
}
/***************静态分配自旋锁并使用 portMUX_INITIALIZER_UNLOCKED 初始化：******************/

//========================================程序入口===========================================
void app_main(void)
{

    /*************静态分配自旋锁并使用 portMUX_INITIALIZE() 初始化：****************/
    // 动态分配自旋锁
    portMUX_TYPE *my_spinlock = malloc(sizeof(portMUX_TYPE));
    // 动态初始化自旋锁
    portMUX_INITIALIZE(my_spinlock);
    taskENTER_CRITICAL(my_spinlock);
    // 访问资源
    taskEXIT_CRITICAL(my_spinlock);
    /*************静态分配自旋锁并使用 portMUX_INITIALIZE() 初始化：****************/

    // Task句柄，用于之后相应操作，假如不操作可以产生
    TaskHandle_t task_handle3;

    int param1 = 5;
    int param2[3] = {1, 2, 3};
    char *param3 = "Hello FreeRTOS!";
    info_t param4 = {
        .name = "LiHua",
        .age = 15};
    // 创建任务并与实现函数关联，分配堆栈空间(Byte)，传入参数，设定优先级0-24，传入句柄
    xTaskCreate(My_task1, "Task1", 2048, (void *)&param1, 10, NULL);
    xTaskCreate(My_task2, "Task2", 2048, (void *)param2, 10, NULL);
    xTaskCreate(My_task3, "Task3", 2048, (void *)param3, 10, &task_handle3);
    xTaskCreate(My_task4, "Task4", 2048, (void *)&param4, 10, NULL);

    vTaskDelay(5000 / portTICK_PERIOD_MS);
    // 任务删除，可以在任务内部删除，也可以在主程序中
    if (task_handle3 != NULL)
    {
        vTaskDelete(task_handle3);
        printf("shanchu -- Task3\n");
    }
    for (int i = 20; i >= 0; i--)
    {
        printf("Restarting in %d seconds...\n", i);
        if (i < 2)
        {
            i = 20;
        }

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

#elif 0

#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h" //使用消息缓冲区必须要有这个头文件
#include "freertos/event_groups.h"   //xEventGroupCreate(void)
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_chip_info.h"

// 定义消息缓冲区句柄
MessageBufferHandle_t xMessageBuffer;
const size_t xMessageBufferSizeBytes = 100;

// 创建队列并发布值的任务
void vATask(void *pvParameters)
{
    size_t xBytesSent;
    uint8_t ucArrayToSend[] = {'0', '1', '2', '3', 0};
    char *pcStringToSend = "String to send";
    const TickType_t x100ms = pdMS_TO_TICKS(100);
    // 创建消息缓冲区
    xMessageBuffer = xMessageBufferCreate(xMessageBufferSizeBytes);
    if (xMessageBuffer == NULL)
    {
        printf("message buffer Create fail!\n");
    }
    else
    {
        printf("message buffer Create successed!\n");
    }

    while (1)
    { // 消息缓存区发送的第一种方式
        // 将数组发送到消息缓冲区，最多阻塞 100 毫秒，以等待消息缓冲区中有足够的可用空间。
        xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)ucArrayToSend, sizeof(ucArrayToSend), x100ms);

        if (xBytesSent != sizeof(ucArrayToSend))
        {
            // 在缓冲区中有足够的空间用于写入数据之前，对 xMessageBufferSend() 的调用超时。
        }

        // 消息缓存区发送的第二种方式
        //  将字符串发送到消息缓冲区。如果缓冲区空间不足，则立即返回。strlen 计算字符串长度不含结尾"\0"
        xBytesSent = xMessageBufferSend(xMessageBuffer, (void *)pcStringToSend, strlen(pcStringToSend), 0);
        if (xBytesSent != strlen(pcStringToSend))
        {
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

// 从队列接收的任务。
void vADifferentTask(void *pvParameters)
{
    while (1)
    {
        uint8_t ucRxData[20];
        size_t xReceivedBytes;
        const TickType_t xBlockTime = pdMS_TO_TICKS(20);
        // 接收消息
        // 从消息缓冲区接收下一条消息。在阻塞状态下等待（因此不使用任何 CPU 处理时间）最多 100 毫秒，消息才可用。
        xReceivedBytes = xMessageBufferReceive(xMessageBuffer,
                                               (void *)ucRxData,
                                               sizeof(ucRxData),
                                               xBlockTime);

        if (xReceivedBytes > 0)
        {
            printf(" MessageBuffer: ucRxData is %d long. ucRxData : %s \n", xReceivedBytes, ucRxData);
            // 这里的消息......
        }
    }
}
void app_main(void)
{
    EventGroupHandle_t xEventGroup1;
    EventGroupHandle_t xEventGroup2;
    /* 打印芯片信息 */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    xTaskCreate(vATask, "vATask", 1024 * 2, NULL, configMAX_PRIORITIES - 3, NULL);
    xTaskCreate(vADifferentTask, "vADifferentTask", 1024 * 2, NULL, configMAX_PRIORITIES - 4, NULL);
    /*
    xEventGroup1 =xEventGroupCreate();//创建新的事件组
    xEventGroup2 =xEventGroupCreate();
    if (xEventGroup1 == NULL)
    {
        printf("创建xEventGroup1失败\r\n");
    }else
    {
        printf("创建xEventGroup1成功\r\n");
    }
    if (xEventGroup2 == NULL)
    {
        printf("创建xEventGroup2失败\r\n");
    }else
    {
        printf("创建xEventGroup2成功\r\n");
    }
    */
    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

#elif 0
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/timer.h"
#include "esp_timer.h"
#include "driver/uart.h"

#define TXD1_PIN (GPIO_NUM_17) // 串口1的发送数据引脚
#define RXD1_PIN (GPIO_NUM_16) // 串口1的接收数据引脚
#define BUF_SIZE (1024)        // 接收数据缓存大小,该大小需要大于内部FIFO大小:UART_FIFO_LEN(128)

#define BUF_SEND_SIZE (1024) // 发送数据缓存大小,该大小需要大于内部FIFO大小:UART_FIFO_LEN(128)

static QueueHandle_t QueueHandle_t_uart1;

void Uart_DataAnalysis(uint8_t *str, int size);
int strfindsub(const char *haystack, char ch);
char *mysubstr(char *srcstr, int offset, int length);
/*串口任务*/
static void uart_task(void *arg)
{
    /*配置串口参数*/
    uart_config_t uart_config = {
        .baud_rate = 115200,                   // 波特率
        .data_bits = UART_DATA_8_BITS,         // 数据位8位
        .parity = UART_PARITY_DISABLE,         // 无奇偶校验
        .stop_bits = UART_STOP_BITS_1,         // 停止位1位
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, // 不使用硬件流控
        .source_clk = UART_SCLK_APB,           // 串口使用的时钟
    };
    /*初始化串口1*/
    uart_driver_install(UART_NUM_1,
                        BUF_SIZE,             // 串口1接收缓存大小
                        BUF_SEND_SIZE,        // 串口1发送缓存大小
                        10,                   // 队列大小为10
                        &QueueHandle_t_uart1, // 缓存管理
                        0                     // 设置串口中断优先级,设置为0意味着让系统从1-3级中自动选择一个
    );
    /*设置串口参数*/
    uart_param_config(UART_NUM_1, &uart_config);
    /*设置串口的TX,RX,RTS,DTR引脚*/ // 不使用RTS,DTR
    uart_set_pin(UART_NUM_1, TXD1_PIN, RXD1_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    // 设置串口模式 485半双工通讯模式
    // ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));
    // 设置UART TOUT功能的读取超时
    // ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT));
    /*申请一块内存,用于临时存储接收的数据*/
    uint8_t *data = (uint8_t *)malloc(BUF_SIZE);
    uart_event_t event;
    while (1)
    {
        if (xQueueReceive(QueueHandle_t_uart1, (void *)&event, portMAX_DELAY))
        {
            switch (event.type)
            {
            case UART_DATA: // 接收到数据
                // 读取接收的数据
                uart_read_bytes(UART_NUM_1, data, event.size, portMAX_DELAY);
                Uart_DataAnalysis(data, event.size);
                // 返回接收的数据
                uart_write_bytes(UART_NUM_1, (const char *)data, event.size);
                break;
            case UART_FIFO_OVF: // FIFO溢出(建议加上数据流控制)
                uart_flush_input(UART_NUM_1);
                xQueueReset(QueueHandle_t_uart1);
                break;
            case UART_BUFFER_FULL: // 接收缓存满(建议加大缓存 BUF_SIZE)
                uart_flush_input(UART_NUM_1);
                xQueueReset(QueueHandle_t_uart1);
                break;
            case UART_BREAK: // 检测到接收数据中断
                break;
            case UART_PARITY_ERR: // 数据校验错误
                break;
            case UART_FRAME_ERR: // 数据帧错误
                break;
            case UART_PATTERN_DET: // 接收到相匹配的字符(没用到)
                break;
            default:
                break;
            }
        }
    }
    free(data);
    data = NULL;
    vTaskDelete(NULL);
}
// 串口事件信息分析处理
void Uart_DataAnalysis(uint8_t *str, int size)
{
    char *rest = NULL;
    int mark = 0;
    char *value = NULL;
    char scmd[2] = {0};
    char delim = ':';
    char *stemp = (char *)str;

    printf("read usrt:%s\n", stemp);

    rest = strncpy(scmd, stemp, 1); // 获取命令
    if (rest == NULL)
    {
        printf("uart input scmd format is error.\n");
        return;
    }
    mark = strfindsub(stemp, delim);
    value = mysubstr(stemp, mark + 1, (size - mark - 1)); // 获取命令携带的信息
    int cmd = atoi(scmd);
    /*
    switch (cmd)
    {
        case FCT_CMD_WRITESN_FCT:         // 写入SN
            printf("FCT write sn read value:%s\n", (char *)value);
            break;
        case FCT_CMD_REQUEST_FCT: // 请求进入FCT
            printf("FCT start read value:%s\n", (char *)value);
            break;
        case FCT_CMD_SET_NET_STAR: // 开始配网

            break;
        case FCT_CMD_SET_NET_STEP: // 断开网络

            break;
        case FCT_CMD_SET_WIFI_INFO: // 获取wifi信息

            break;
        default:
            break;
    }
    */
}
// 获取字符串中固定字符第一次出现的位置
int strfindsub(const char *haystack, char ch)
{
    int cnt = 0;
    while (haystack[cnt] != ch)
    {
        cnt++;
    }

    return cnt;
}
// 拷贝字符串指定位置间的子字符串
char *mysubstr(char *srcstr, int offset, int length)
{
    assert(length > 0);
    assert(srcstr != NULL);

    int total_length = strlen(srcstr); // 首先获取srcstr的长度
    // 判断srcstr的长度减去需要截取的substr开始位置之后，剩下的长度
    // 是否大于指定的长度length，如果大于，就可以取长度为length的子串
    // 否则就把从开始位置剩下的字符串全部返回。
    int real_length = ((total_length - offset) >= length ? length : (total_length - offset)) + 1;
    char *tmp;
    if (NULL == (tmp = (char *)malloc(real_length * sizeof(char))))
    {
        printf("Memory overflow . \n");
        exit(0);
    }
    strncpy(tmp, srcstr + offset, real_length - 1);
    tmp[real_length - 1] = '\0';

    return tmp;
}
void app_main(void)
{
    xTaskCreate(uart_task, "uart_task", 2048, NULL, 10, NULL);
    while (1)
    {
        vTaskDelay(10 / portTICK_PERIOD_MS); // 定时器延时
    }
}

#elif 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "esp_log.h"
struct AMessage
{
    char ucMessageID;
    char ucData[20];
} xMessage;

QueueHandle_t xQueue;

// Task to create a queue and post a value.
void vATask(void *pvParameters)
{
    struct AMessage *pxMessage;

    // Create a queue capable of containing 10 pointers to AMessage structures.
    // These should be passed by pointer as they contain a lot of data.
    xQueue = xQueueCreate(10, sizeof(struct AMessage *));
    if (xQueue == 0)
    {
        // Failed to create the queue.
    }

    // ...

    // Send a pointer to a struct AMessage object.  Don't block if the
    // queue is already full.
    pxMessage = &xMessage;
    xQueueSend(xQueue, (void *)&pxMessage, (TickType_t)0);

    // ... Rest of task code.
}

// Task to receive from the queue.
void vADifferentTask(void *pvParameters)
{
    struct AMessage *pxRxedMessage;

    if (xQueue != 0)
    {
        // Receive a message on the created queue.  Block for 10 ticks if a
        // message is not immediately available.
        if (xQueueReceive(xQueue, &(pxRxedMessage), (TickType_t)10))
        {
            // pcRxedMessage now points to the struct AMessage variable posted
            // by vATask.
        }
    }

    // ... Rest of task code.
}

#elif 0

/*********************************************************************
 * INCLUDES
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "esp_log.h"

#define BUF_SIZE (1024)
#define UART_MAX_NUM_RX_BYTES (1024)

static void uartEventTask(void *pvParameters);

/*********************************************************************
 * LOCAL VARIABLES
 */
static QueueHandle_t s_uart0Queue;

static const char *TAG = "board_uart";

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief 串口驱动初始化
 @param 无
 @return 无
*/
void UART_Init(void)
{
    // Configure parameters of an UART driver,
    // communication pins and install the driver
    uart_config_t uart_config =
        {
            .baud_rate = 115200,
            .data_bits = UART_DATA_8_BITS,
            .parity = UART_PARITY_DISABLE,
            .stop_bits = UART_STOP_BITS_1,
            .flow_ctrl = UART_HW_FLOWCTRL_DISABLE};

    uart_param_config(UART_NUM_0, &uart_config);                                                              // 配置串口0参数
    uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); // 配置串口0引脚
    // Install UART driver, and get the queue.
    uart_driver_install(UART_NUM_0, BUF_SIZE * 2, BUF_SIZE * 2, 100, &s_uart0Queue, 0); // 安装UART驱动程序

    // Create a task to handler UART event from ISR
    xTaskCreate(uartEventTask, "uartEventTask", 2048, NULL, 12, NULL);
}

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void uartEventTask(void *pvParameters)
{
    uart_event_t event;
    uint8_t *pTempBuf = (uint8_t *)malloc(UART_MAX_NUM_RX_BYTES);

    for (;;)
    {
        // Waiting for UART event.
        if (xQueueReceive(s_uart0Queue, (void *)&event, (TickType_t)10)) // portTICK_PERIOD_MS,( TickType_t ) 10,(portTickType)portMAX_DELAY)
        {
            bzero(pTempBuf, UART_MAX_NUM_RX_BYTES);

            switch (event.type)
            {
            // Event of UART receving data
            // We'd better handler data event fast, there would be much more data events than
            // other types of events. If we take too much time on data event, the queue might be full.
            case UART_DATA:
                // ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                uart_read_bytes(UART_NUM_0, pTempBuf, event.size, portMAX_DELAY);
                uart_write_bytes(UART_NUM_0, (const char *)pTempBuf, event.size);
                break;

            // Event of HW FIFO overflow detected
            case UART_FIFO_OVF:
                ESP_LOGI(TAG, "hw fifo overflow");
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(UART_NUM_0);
                xQueueReset(s_uart0Queue);
                break;

            // Event of UART ring buffer full
            case UART_BUFFER_FULL:
                ESP_LOGI(TAG, "ring buffer full");
                // If buffer full happened, you should consider encreasing your buffer size
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(UART_NUM_0);
                xQueueReset(s_uart0Queue);
                break;

            case UART_PARITY_ERR:
                ESP_LOGI(TAG, "uart parity error");
                break;

            // Event of UART frame error
            case UART_FRAME_ERR:
                ESP_LOGI(TAG, "uart frame error");
                break;

            // Others
            default:
                ESP_LOGI(TAG, "uart event type: %d", event.type);
                break;
            }
        }
    }

    free(pTempBuf);
    pTempBuf = NULL;
    vTaskDelete(NULL);
}

void app_main(void)
{
    UART_Init();
}

#elif 0

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_err.h"

#define UART1_TX_BUF_SIZE 134 /* 256个字节 */
#define UART1_RX_BUF_SIZE 134 /* 256个字节 */
#define UART1_RX GPIO_NUM_16  /* 256个字节 */
#define UART1_TX GPIO_NUM_17  /* 256个字节 */

static const char *TAG = "UART section0";

static char msg_test[] = "hello,world"; /* 测试使用的字符串 */
static char buffer[UART1_RX_BUF_SIZE];  /* 暂时存储从串口接收到的字符串 */

/* 串口1的配置 */
const uart_config_t uart1_config =
    {
        .baud_rate = 115200,                   /* 通讯波特率 */
        .data_bits = UART_DATA_8_BITS,         /* 每一个数据是8位 */
        .parity = UART_PARITY_DISABLE,         /* 关闭奇偶校验 */
        .stop_bits = UART_STOP_BITS_1,         /* 停止位是1位 */
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, /* 软件控流 */
        .source_clk = UART_SCLK_APB,           /* APB时钟 */
};

void app_main(void)
{
    ESP_LOGI(TAG, "Example configured to uart communication");

    /* 设置串口1的参数 */
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart1_config));

    /* 设置串口的gpio口，esp32支持gpio口动态设置，这一次先使用默认的串口gpio */
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, UART1_TX, UART1_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

    /* 启动串口1 */
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1,        /* 串口1 */
                                        UART1_TX_BUF_SIZE, /* 发送FIFO的大小 */
                                        UART1_RX_BUF_SIZE, /* 接受FIFO的大小 */
                                        0,                 /* 不使用queue */
                                        NULL,              /* 因为不使用queue，所以NULL */
                                        0)                 /* 不分配中断标志 */
    );

    while (1)
    {
        // uart_write_bytes(UART_NUM_1, msg_test, strlen(msg_test)); /* 将字符串“hello,world"放入串口1的TX_FIFO */

        // uart_write_bytes(UART_NUM_0,msg_test1,strlen(msg_test1));  /* 将字符串“hello,world"放入串口1的TX_FIFO */

        vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时300ms */

        int len = uart_read_bytes(UART_NUM_1, buffer, (UART1_RX_BUF_SIZE - 1), 20 / portTICK_PERIOD_MS); /* 从串口1的RX_FIFO获取字符串 */

        // int len1 = uart_read_bytes(UART_NUM_0, buffer1, (UART1_RX_BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        /* 如果读到包的话 */
        if (len)
        {
            buffer[len] = '\0';                                                                        /* 在结尾加入字符'\0'， */
            ESP_LOGI(TAG, "Recv str -> %s , and the length is:%d", buffer, strlen(buffer));            /* 打印logo */
            ESP_LOGI(TAG, "The size of buffer is %d,and ready to clear this buffer.", sizeof(buffer)); /* 打印logo */
            uart_write_bytes(UART_NUM_1, buffer, strlen(buffer));
            memset(buffer, 0, sizeof(buffer)); /* 清空内存，等待下一次的串口保文。 */
        }
    }
}

#elif 0 // 串口
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h" //sdk配置
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"           //任务
#include "freertos/message_buffer.h" //使用消息缓冲区必须要有这个头文件
#include "esp_chip_info.h"           //芯片_信息
#include "esp_flash.h"               //内存
#include "esp_system.h"              //系统
#include "esp_spi_flash.h"
#include "driver/uart.h"            //串口
#include "esp_log.h"                //日志
#include "gatts_table_creat_demo.h" //ble蓝牙
#include "driver/gpio.h"            //GPIO
#include "driver/ledc.h"            //LEDPWM
#include "driver/mcpwm.h"           //电机LEDPWM

static const char *TAG = "example1";
/*串口*/
#define UART1_TX_BUF_SIZE 132       /* 256个字节 */
#define UART1_RX_BUF_SIZE 132       /* 256个字节 */
#define UART1_RX GPIO_NUM_16        /* 256个字节 */
#define UART1_TX GPIO_NUM_17        /* 256个字节 */
static char msg_test[] = "hello,world"; /* 测试使用的字符串 */
static char buffer[UART1_RX_BUF_SIZE];  /* 暂时存储从串口接收到的字符串 */

/*串口初始化*/
void uartInit()
{
    /* 串口1的配置 */
    const uart_config_t uart1_config =
        {
            .baud_rate = 115200,                   /* 通讯波特率 */
            .data_bits = UART_DATA_8_BITS,         /* 每一个数据是8位 */
            .parity = UART_PARITY_DISABLE,         /* 关闭奇偶校验 */
            .stop_bits = UART_STOP_BITS_1,         /* 停止位是1位 */
            .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, /* 软件控流 */
            .source_clk = UART_SCLK_APB,           /* APB时钟 */
        };
    /* 设置串口1的参数 */
    ESP_ERROR_CHECK(uart_param_config(UART_NUM_1,      /*UART 端口号，最大端口号为(UART_NUM_MAX -1)。*/
                                      &uart1_config)); /*UART 参数设置*/

    /* 设置串口的gpio口，esp32支持gpio口动态设置，这一次先使用默认的串口gpio */
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1,           /*UART 端口号*/
                                 UART1_TX,             /*UART TX 引脚 GPIO 编号*/
                                 UART1_RX,             /*UART RX 引脚 GPIO 编号*/
                                 UART_PIN_NO_CHANGE,   /*UART RTS 引脚 GPIO 编号*/
                                 UART_PIN_NO_CHANGE)); /*CTS 引脚 GPIO 编号*/

    /* 启动串口1 */
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1,        /* 串口1 */
                                        UART1_TX_BUF_SIZE, /* 发送FIFO的大小 */
                                        UART1_RX_BUF_SIZE, /* 接受FIFO的大小 */
                                        0,                 /* 不使用queue */
                                        NULL,              /* 因为不使用queue，所以NULL */
                                        0)                 /* 不分配中断标志 */
    );
}

/*接收数据*/
void uartH()
{
    const uart_port_t uart_num = UART_NUM_1;
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
        .rx_flow_ctrl_thresh = 122,
    };
    // 配置串口参数
    ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
    int len = 0;
    while (1)
    {
        // UART 从 UART 缓冲区读取字节,从串口1的RX_FIFO获取字符串
        len = uart_read_bytes(UART_NUM_1,               // UART端口号
                              buffer,                   // 指向缓冲区的指针
                              (UART1_RX_BUF_SIZE - 1),  // 数据长度
                              20 / portTICK_PERIOD_MS); // 等待时间
        // 如果读到包的话
        if (len)
        {
            printf("len  =======   %d\n", len);
            buffer[len] = '\0';                                                             // 在结尾加入字符'\0'，
            ESP_LOGI(TAG, "Recv str -> %s , and the length is:%d", buffer, strlen(buffer)); // 打印logo
            // uart_write_bytes(UART_NUM_1, buffer, strlen(buffer));//发送数据到串口1
            memset(buffer, 0, sizeof(buffer)); // 清空内存，等待下一次的串口报文。
        }
    }
}

void app_main(void)
{
    uartInit();
    void uartH();
}

#elif 0 // 中断,二进制信号量

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h" //二进制信号量xSemaphoreCreateBinary()
#include "driver/gpio.h"
#include "esp_log.h"

#define TAG "app"

#define ESP_INTR_FLAG_DEFAULT 0

static SemaphoreHandle_t gpio_sem;

#define GPIO_DEMO_PIN GPIO_NUM_18

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    xSemaphoreGiveFromISR(gpio_sem, NULL);
}

static void gpio_task_example(void *arg)
{
    for (;;)
    {
        if (xSemaphoreTake(gpio_sem, portMAX_DELAY))
        {
            ESP_LOGI(TAG, "GPIO[%d] intr, val: %d\n", GPIO_DEMO_PIN, gpio_get_level(GPIO_DEMO_PIN));
        }
    }
}

void app_main(void)
{
    // install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);

    // zero-initialize the config structure.
    gpio_config_t io_conf = {};

    // interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    io_conf.pin_bit_mask = 1ULL << GPIO_DEMO_PIN;
    // set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    // enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    // 二进制信号量
    gpio_sem = xSemaphoreCreateBinary();
    // start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    gpio_isr_handler_add(GPIO_DEMO_PIN, gpio_isr_handler, NULL);

    // 如果某个时刻不想产生中断可以将其从中断服务中删除
    // gpio_isr_handler_remove(GPIO_DEMO_PIN);
}
#elif 0 // 中断,2个IO口
#include <freertos/FreeRTOS.h>  
#include <freertos/task.h>  
#include <driver/gpio.h>  
  
#define FIRST_INTERRUPT_PIN GPIO_NUM_2  
#define SECOND_INTERRUPT_PIN GPIO_NUM_4  
  
// 用于第一个GPIO的配置和引脚选择  
static gpio_num_t gpio_num1 = FIRST_INTERRUPT_PIN;  
static gpio_pad_select_gpio_t gpio_pad1 = {.gpio_num = FIRST_INTERRUPT_PIN};  
static const gpio_config_t gpio_config1 = {  
    .pin_bit_mask = 1 << gpio_num1,  
    .mode = GPIO_MODE_INPUT,  
    .pull_up_en = 0,  
    .pull_down_en = 0,  
    .intr_type = GPIO_INTR_ANYEDGE,  
};  
  
// 用于第二个GPIO的配置和引脚选择  
static gpio_num_t gpio_num2 = SECOND_INTERRUPT_PIN;  
static gpio_pad_select_gpio_t gpio_pad2 = {.gpio_num = SECOND_INTERRUPT_PIN};  
static const gpio_config_t gpio_config2 = {  
    .pin_bit_mask = 1 << gpio_num2,  
    .mode = GPIO_MODE_INPUT,  
    .pull_up_en = 0,  
    .pull_down_en = 0,  
    .intr_type = GPIO_INTR_ANYEDGE,  
};  
  
void IRAM_ATTR gpio_isr_handler(void* arg) {  
    uint32_t gpio_num = (uint32_t) arg;  
  
    if (gpio_num == gpio_num1) {  
        // 第一个GPIO引脚的中断处理逻辑  
        // ...  
        printf("Interrupt on GPIO %d\n", gpio_num);  
    } else if (gpio_num == gpio_num2) {  
        // 第二个GPIO引脚的中断处理逻辑  
        // ...  
        printf("Interrupt on GPIO %d\n", gpio_num);  
    }  
}  
  
void app_main() {  
    // 初始化第一个GPIO  
    gpio_pad_select_gpio(gpio_pad1);  
    gpio_configure(gpio_num1, &gpio_config1);  
    gpio_isr_handler_add(gpio_num1, gpio_isr_handler, (void*) gpio_num1);  
  
    // 初始化第二个GPIO  
    gpio_pad_select_gpio(gpio_pad2);  
    gpio_configure(gpio_num2, &gpio_config2);  
    gpio_isr_handler_add(gpio_num2, gpio_isr_handler, (void*) gpio_num2);  
  
    // 安装GPIO中断服务例程  
    gpio_install_isr_service();  
}

#elif 0 // 中断,消息传递

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h" //二进制信号量xSemaphoreCreateBinary()

#define LED GPIO_NUM_2
#define ZHONG_DUAN_1 GPIO_NUM_17

// 消息队列句柄
static QueueHandle_t s_queue_led = NULL;

static QueueHandle_t gpio_sem;

// 中断处理函数
static void IRAM_ATTR ledIsr(void *arg)
{
    // 向队列写true或false,虽然变量传的是地址，实际上是值传递
    if (xQueueSendFromISR(s_queue_led, (bool *)arg, NULL) != pdPASS)
        return;
    *(bool *)arg = !*(bool *)arg; // 真假翻转
}

// 与main并发的任务
void task_led(void *arg)
{
    // 配置GPIO2输出，此脚连接led
    gpio_set_direction(LED, GPIO_MODE_OUTPUT);
    bool led_flag = true;
    int ret = 0;

    printf("led005\r\n");
    while (true)
    {
        // 非阻塞读，读错或队列空不影响led闪烁
        ret = xQueueReceive(s_queue_led, &led_flag, 0);

        if (led_flag)
        {
            printf("led正在闪烁...%d\n", led_flag);
            gpio_set_level(LED, 1);
            vTaskDelay(500 / portTICK_PERIOD_MS);
            gpio_set_level(LED, 0);
        }
        else if (ret == pdPASS)
        {
            printf("led停止闪烁%d\n", led_flag);
            gpio_set_level(LED, 0);
        }

        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

// 单个IO口中断初始化,参数1:IO口编号,参数2,teue为高电平上沿
bool zhong_duan_init(uint64_t ioKou, bool shangXia)
{
    if (shangXia)
    {
        if (gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1) != ESP_OK)
        {
            printf("led中断服务错误,函数返回\r\n");
            return false;
        }
        gpio_reset_pin(ZHONG_DUAN_1);
        gpio_config_t io_t = {};
        io_t.intr_type = GPIO_INTR_POSEDGE;
        io_t.pin_bit_mask = 1ULL << ZHONG_DUAN_1;
        io_t.mode = GPIO_MODE_INPUT;
        io_t.pull_down_en = GPIO_PULLDOWN_ENABLE;
        gpio_config(&io_t);
        return true;
    }
    else
    {
        if (gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1) != ESP_OK)
        {
            printf("led中断服务错误,函数返回\r\n");
            return false;
        }
        gpio_reset_pin(ZHONG_DUAN_1);
        gpio_config_t io_t = {};
        io_t.intr_type = GPIO_INTR_POSEDGE;
        io_t.pin_bit_mask = 1ULL << ZHONG_DUAN_1;
        io_t.mode = GPIO_MODE_INPUT;
        io_t.pull_up_en = GPIO_PULLUP_ENABLE;
        gpio_config(&io_t);
        return true;
    }
}

void app_main(void)
{
    gpio_reset_pin(LED); // 清除GPIO状态
    // 配置GPIO18下降沿中断并指定中断函数
    if (false == zhong_duan_init(17, true))
    {
        printf("中断服务错误,请检查...\r\n");
        return;
    }

    printf("led001\r\n");
    // 创建消息队列
    s_queue_led = xQueueCreate(1, sizeof(bool));
    // 创建二进制信号量
    gpio_sem = xSemaphoreCreateBinary();

    if (s_queue_led == NULL)
    {
        printf("消息队列创建失败,函数返回\r\n");
        return;
    }
    // 创建任务
    if (xTaskCreate(task_led, "led", 2048, NULL, 10, NULL) == pdFALSE)
    {
        printf("task_led创建失败,函数返回\r\n");
        return;
    }
    printf("led001-1\r\n");

    /*中断处理函数*/
    static bool s_isr_flag = true;
    gpio_isr_handler_add(ZHONG_DUAN_1, ledIsr, &s_isr_flag);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("led002\r\n");

    while (true)
    {
        printf("只因你太美\n");
        vTaskDelay(4500 / portTICK_PERIOD_MS);
    }
}

#elif 0

/* WiFi station Example
   This example code is in the Public Domain (or CC0 licensed, at your option.)
   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"

/* 这些示例使用 WiFi 配置，您可以通过项目配置菜单进行设置。如果您不愿意，只需将以下条目更改为具有您想要的配置的字符串 -
即 #define Examples_WIFI_SSID &quot;mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID "cccxxx"
#define EXAMPLE_ESP_WIFI_PASS "chenxin1234567890"
#define EXAMPLE_ESP_MAXIMUM_RETRY 5

/* FreeRTOS 事件组在我们连接时发出信号*/
static EventGroupHandle_t s_wifi_event_group;

/* 事件组允许每个事件有多个位，但我们只关心两个事件：
 * -我们已通过 IP 连接到 AP
 * - 在达到最大重试次数后，我们无法连接 */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static const char *TAG = "wifi station";

static int s_retry_num = 0;

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false},
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(s_wifi_event_group);
}

void app_main(void)
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
}

#elif 0 // WIFI连接

#include <nvs_flash.h>
#include <esp_event.h>
#include <esp_wifi.h>
#include <esp_log.h>
#include <esp_netif.h>
#include <esp_netif_ip_addr.h>

#include <stdio.h>
#include <lwip/netif.h>

static const char *TAG = "WIFI: ";

/**
 * @brief 用于初始化nvs
 */
void init_nvs()
{
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

/**
 * @brief WiFi 的事件循环Handler
 * @param arg
 * @param event_base
 * @param event_id
 * @param event_data
 */
void wifi_event_handler(void *arg,                   // 这是一个指向任意类型的指针，通常用于传递给事件处理器的自定义参数
                        esp_event_base_t event_base, // 这个参数表示事件来自哪个事件源
                        int32_t event_id,            // 这个参数表示事件的ID
                        void *event_data             // 这个参数通常是一个指向特定数据结构的指针
)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }

    if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI("TEST_ESP32", "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
        // ESP_LOGI("TEST_ESP32", "IP: %d.%d.%d.%d", (&event->ip_info.ip));
    }
}

void app_main(void)
{
    init_nvs();

    esp_netif_init();

    esp_event_loop_create_default();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    //  注：下方的cfg_sta也可以写成这样
    //  wifi_config_t wifi_config = {
    //      .sta = {
    //          .ssid = "SSID",
    //          .password = "密码",
    //      }
    //  };
    wifi_sta_config_t cfg_sta = {
        .ssid = "cccxxx",
        .password = "chenxin1234567890",
    };
    //  而直接将wifi_sta_config_t(或指针)转为wifi_config_t(或指针)是GCC的拓展语法，如下
    esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&cfg_sta);

    esp_wifi_set_mode(WIFI_MODE_STA);

    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL);
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL);

    esp_wifi_start();
}

#elif 0 //成功连接,ESP32S模块不能连接

#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#define LIGHT_ESP_WIFI_SSID "cccxxx"
#define LIGHT_ESP_WIFI_PASS "chenxin1234567890"
#define LIGHT_ESP_MAXIMUM_RETRY 500

// 事件组允许每个事件有多个位，这里只使用两个事件：
// 已经连接到AP并获得了IP
// 在最大重试次数后仍未连接
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static const char *TAG = "wifi: ";

// FreeRTOS事件组，连接成功时发出信号
static EventGroupHandle_t s_wifi_event_group = NULL;
static int s_retry_num = 0;

// 事件回调
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    // 如果是Wi-Fi事件，并且事件ID是Wi-Fi事件STA_START
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        // 如果是Wi-Fi事件，并且事件ID是Wi-Fi断开事件
        /* 如果重试次数小于最大重试次数 */
        if (s_retry_num < LIGHT_ESP_MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            if (s_retry_num > 200)
            {
                vTaskDelay(pdMS_TO_TICKS(60000));
                if (s_retry_num > 400)
                {
                    s_retry_num = 0;
                }
            }
            s_retry_num++;
            ESP_LOGI(TAG, "重试连接AP\n");
            vTaskDelay(pdMS_TO_TICKS(6000));
        }
        else
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "连接AP失败\r\n");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        // 如果是IP事件，并且事件ID是IP事件STA_GOT_IP

        // 获取事件结果
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));

        // 将重试次数重置为 0；
        s_retry_num = 0;
        // 通过调用 xEventGroupSetBits 函数，将 WIFI_CONNECTED_BIT 设置到事件组中，表示成功连接到 AP
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

static void wifi_initialize(void)
{
    // 创建一个事件组，用于管理Wi-Fi连接事件。
    s_wifi_event_group = xEventGroupCreate();

    // 初始化 TCP/IP 协议栈。
    ESP_ERROR_CHECK(esp_netif_init());

    // 创建默认事件循环。
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 创建默认的Wi-Fi网络接口。
    esp_netif_create_default_wifi_sta();
    // 设置 Wi-Fi 初始化配置为默认配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件处理器，以处理 Wi-Fi 和 IP 相关事件
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
}

static void wifi_station_initialize(void)
{
    // 配置WiFi的配置信息
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = LIGHT_ESP_WIFI_SSID,
            .password = LIGHT_ESP_WIFI_PASS,
            // 启用WPA2模式，常用的WiFi连接方式  WIFI_AUTH_WPA2_PSK
            .threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false},
        },
    };
    // WiFi工作模式设置为STA
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // 设置WiFi工作模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    // 启动WiFi
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_station_initialize finished.");

    /* 等待连接建立（WIFI_CONNECTED_BIT）或连接失败的次数达到最大值（WIFI_FAIL_BIT）。
     * 这些位通过 event_handler() 设置（详见上面）*/
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

    /* xEventGroupWaitBits() 返回调用前的 bits，因此我们可以测试实际发生了什么事件。 */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "连接到 ap SSID:%s password:%s\n", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "无法连接到 SSID:%s, password:%s\n", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    }
    else
    {
        ESP_LOGE(TAG, "意外事件\n");
    }
}

void app_main()
{
    int i = 0;
    ESP_LOGE(TAG, "app_main");
    // 初始化NVS存储区
    ESP_ERROR_CHECK(nvs_flash_init());

    // Wi-Fi初始化
    ESP_LOGI(TAG, "Wi-Fi initialization");
    wifi_initialize();

    // Wi-Fi Station初始化
    wifi_station_initialize();

    while (1)
    {
        ESP_LOGI(TAG, "[%02d] Hello world!", i++);
        vTaskDelay(pdMS_TO_TICKS(10000));
    }
}

#elif 0 // 任务线程数量

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void task_led(void *arg)
{
    int *a = (int *)arg;
    printf("zhe shi di de  = %p \n", &a);
    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    void *const pvParameters;
    int i = 0;
    for (i = 0; i < 10000; i++)
    {
        xTaskCreate(task_led, "led", 2048, &i, 10, NULL);
        printf("di ji de  = %d \n", i);
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
    printf("wanceng  = %d \n", i);
}

#endif